package com.xnyzc.lhy.nio.config.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.xnyzc.lhy.common.component.service.ICacheService;
import com.xnyzc.lhy.common.entity.RedisEnum;
import com.xnyzc.lhy.common.exception.PangException;
import com.xnyzc.lhy.common.penum.EErrorCode;
import com.xnyzc.lhy.common.util.CheckUtil;
import com.xnyzc.lhy.common.util.ListUtil;
import com.xnyzc.lhy.nio.config.RedisConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 缓存操作实现类
 * @Author: scootXin
 * @Date: 2019/4/12 11:11
 */
@Slf4j
@Service
@AutoConfigureAfter(RedisConfig.class)
public class CacheServiceImpl implements ICacheService {
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    @Qualifier("persistenceRedisTemplate")
    private RedisTemplate persistenceRedisTemplate;

    /**
     * 检查数据
     *
     * @param redisEnum
     */
    @Override
    public boolean check(RedisEnum redisEnum) {
        String key = redisEnum.getRedisKey();

        if (redisEnum.isPersistence()) {
            return persistenceRedisTemplate.hasKey(key);
        }
        else {
            return redisTemplate.hasKey(key);
        }
    }

    /**
     * 检查数据
     *
     * @param redisEnum
     */
    @Override
    public <PK> boolean check(RedisEnum redisEnum, PK pk) {
        String key = redisEnum.getRedisKey();
        key = key + ":" + pk;

        if (redisEnum.isPersistence()) {
            return persistenceRedisTemplate.hasKey(key);
        }
        else {
            return redisTemplate.hasKey(key);
        }
    }
    @Override
    public <PK, T> void hashAddOrUpdate(RedisEnum redisEnum, PK pk, T t) {
        String key = redisEnum.getRedisKey();

        redisTemplate.opsForHash().put(key, pk, t);
    }

    /**
     * 向hash中新增或修改数据
     *
     * @param redisEnum
     * @param <T>
     */
    @Override
    public <PK, T> void hashAddOrUpdate(RedisEnum redisEnum, Map<PK, T> maps) {
        String key = redisEnum.getRedisKey();

        if (redisEnum.isPersistence()) {
            persistenceRedisTemplate.opsForHash().putAll(key, maps);
        }
        else {
            redisTemplate.opsForHash().putAll(key, maps);
        }
    }
    @Override
    public <PK> void hashDel(RedisEnum redisEnum, PK... pks) {
        String key = redisEnum.getRedisKey();

        if (redisEnum.isPersistence()) {
            persistenceRedisTemplate.opsForHash().delete(key, pks);
        }
        else {
            redisTemplate.opsForHash().delete(key, pks);
        }
    }
    @Override
    public <PK> void hashCascadeAutoDel(RedisEnum redisEnum, PK... pks) {
        List<RedisEnum> group = redisEnum.getGroup();

        for (RedisEnum re : group) {
            hashDel(re, pks);
        }
    }

    @Override
    public <PK> void hashCascadePersonalDel(PK pk, RedisEnum... redisEnums) {
        for (RedisEnum re : redisEnums) {
            hashDel(re, pk);
        }
    }

    @Override
    public <PK, T> T hashGet(RedisEnum redisEnum, Class<T> tClass, PK pk) {
        String key = redisEnum.getRedisKey();

        if (redisEnum.isPersistence()) {
            return change((JSONObject) persistenceRedisTemplate.opsForHash().get(key, pk), tClass);
        }
        else {
            return change((JSONObject) redisTemplate.opsForHash().get(key, pk), tClass);
        }
    }

    @Override
    public <PK, T> List<T> hashGet(RedisEnum redisEnum, Class<T> tClass, PK... pks) {
        String key = redisEnum.getRedisKey();

        List<JSONObject> tmpList = Lists.newLinkedList();

        if (redisEnum.isPersistence()) {
            tmpList = persistenceRedisTemplate.opsForHash().multiGet(key, ListUtil.getListByArray(pks));
        }
        else {
            tmpList = redisTemplate.opsForHash().multiGet(key, ListUtil.getListByArray(pks));
        }

        List<T> rtnList = Lists.newLinkedList();
        for (JSONObject jsonObject : tmpList) {
            rtnList.add(change(jsonObject, tClass));
        }

        return rtnList;
    }

    /**
     * 从hash中获取数据
     *
     * @param redisEnum
     * @param pk
     * @param <PK>
     * @param <T>
     * @return
     */
    @Override
    public <PK, T> List<T> hashGetList(RedisEnum redisEnum, Class<T> tClass, PK pk) {
        String key = redisEnum.getRedisKey();

        JSONArray jsonArray = null;

        if (redisEnum.isPersistence()) {
            jsonArray = (JSONArray) persistenceRedisTemplate.opsForHash().get(key, pk);
        }
        else {
            jsonArray = (JSONArray) redisTemplate.opsForHash().get(key, pk);
        }

        if (jsonArray == null) {
            return Lists.newLinkedList();
        }

        List<T> rtnList = Lists.newLinkedList();
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = (JSONObject) jsonArray.get(i);

            rtnList.add(change(jsonObject, tClass));
        }

        return rtnList;
    }

    @Override
    public <PK> void hashExpire(RedisEnum redisEnum, PK pk, int seconds) {
        String key = redisEnum.getRedisKey();
        key = key + ":" + pk;

        if (redisEnum.isPersistence()) {
            persistenceRedisTemplate.expire(key, seconds, TimeUnit.SECONDS);
        }
        else {
            redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
        }
    }

    /**
     * hash类型计数器新增
     *
     * @param redisEnum
     * @param pk
     */
    @Override
    public <PK> void hashIncrement(RedisEnum redisEnum, PK pk) {
        hashIncrement(redisEnum, pk, 1);
    }

    /**
     * hash类型计数器新增
     *
     * @param redisEnum
     * @param pk
     */
    @Override
    public <PK> void hashIncrement(RedisEnum redisEnum, PK pk, int cnt) {
        String key = redisEnum.getRedisKey();

        if (redisEnum.isPersistence()) {
            persistenceRedisTemplate.opsForHash().increment(key, pk, 1);
        }
        else {
            redisTemplate.opsForHash().increment(key, pk, cnt);
        }
    }

//    @Override
//    public void geoAddOrUpdate(PangGeoPoint... geoPoints) {
//        if (geoPoints == null || geoPoints.length == 0) {
//            return;
//        }
//
//        MultiValueMap<RedisLocationTypeEnum, PangGeoPoint> multiValueMap = new LinkedMultiValueMap<>();
//        for (PangGeoPoint geoPoint : geoPoints) {
//            multiValueMap.add(geoPoint.getRedisLocationTypeEnum(), geoPoint);
//        }
//
//        for (RedisLocationTypeEnum redisLocationTypeEnum : multiValueMap.keySet()) {
//            List<PangGeoPoint> pangGeoPointList = multiValueMap.get(redisLocationTypeEnum);
//            List<RedisGeoCommands.GeoLocation> locationList = Lists.newLinkedList();
//
//            for (PangGeoPoint geoPoint : pangGeoPointList) {
//                if (CheckUtil.objIsEmpty(geoPoint, geoPoint.getPk(), geoPoint.getLongitude(), geoPoint.getLatitude())) {
//                    throw PangException.create(EErrorCode.missingArg, "handler geoPoints:" + JSON.toJSONString(geoPoint));
//                }
//
//                locationList.add(new RedisGeoCommands.GeoLocation(geoPoint.toGeoMessageJson(), new Point(Double.valueOf(geoPoint.getLongitude()),
//                        Double.valueOf(geoPoint.getLatitude()))));
//            }
//
//            persistenceRedisTemplate.opsForGeo().add(redisLocationTypeEnum.getRedisKey(), locationList);
//        }
//    }

    /**
     * 删除GEO标记点
     */
//    @Override
//    public void geoRemove(PangGeoPoint... geoPoints) {
//        if (geoPoints == null || geoPoints.length == 0) {
//            return;
//        }
//
//        MultiValueMap<RedisLocationTypeEnum, PangGeoPoint> multiValueMap = new LinkedMultiValueMap<>();
//        for (PangGeoPoint geoPoint : geoPoints) {
//            multiValueMap.add(geoPoint.getRedisLocationTypeEnum(), geoPoint);
//        }
//
//        for (RedisLocationTypeEnum redisLocationTypeEnum : multiValueMap.keySet()) {
//            List<PangGeoPoint> pangGeoPointList = multiValueMap.get(redisLocationTypeEnum);
//            List<String> meberList = Lists.newLinkedList();
//
//            for (PangGeoPoint geoPoint : pangGeoPointList) {
//                if (CheckUtil.objIsEmpty(geoPoint, geoPoint.getPk(), geoPoint.getLongitude(), geoPoint.getLatitude())) {
//                    throw PangException.create(EErrorCode.missingArg, "handler geoPoints:" + JSON.toJSONString(geoPoint));
//                }
//
//                meberList.add(geoPoint.toGeoMessageJson());
//            }
//
//            persistenceRedisTemplate.opsForGeo().remove(redisLocationTypeEnum.getRedisKey(), meberList.toArray(new String[]{}));
//        }
//    }

//    @Override
//    public List<PangGeoPoint> geoRadius(RedisLocationTypeEnum locationType, String longitude, String latitude, double radius) {
//        if (CheckUtil.objIsEmpty(locationType, longitude, latitude, radius)) {
//            throw PangException.create(EErrorCode.missingArg);
//        }
//
//        Circle circle = new Circle(new Point(Double.valueOf(longitude), Double.valueOf(latitude)), new Distance(radius, Metrics.KILOMETERS));
//
//        RedisGeoCommands.GeoRadiusCommandArgs commandArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance()
//                .includeCoordinates().sortAscending();
//
//        GeoResults geoResults = persistenceRedisTemplate.opsForGeo().radius(locationType.getRedisKey(), circle, commandArgs);
//        List<PangGeoPoint> pangGeoMessageList = Lists.newLinkedList();
//
//        if (geoResults == null) {
//            throw PangException.create(EErrorCode.apiErr, Sb.create().add("error when query geo for:").add(locationType).add(" ").add(longitude)
//                    .add(":").add(latitude).add(" in ").add(radius).toString());
//        }
//
//        List<GeoResult> contents = geoResults.getContent();
//        if (contents == null || contents.size() == 0) {
//            return pangGeoMessageList;
//        }
//
//        for (GeoResult content : contents) {
//            RedisGeoCommands.GeoLocation location = (RedisGeoCommands.GeoLocation) content.getContent();
//            pangGeoMessageList.add(new PangGeoPoint(locationType, location.getPoint().getX(), location.getPoint().getY(), location.getName(),
//                    content.getDistance().getValue()));
//        }
//
//        return pangGeoMessageList;
//    }

    @Override
    public <T> void listRightPush(RedisEnum redisEnum, List<T> list) {
        if (CheckUtil.objIsEmpty(redisEnum, list)) {
            throw PangException.create(EErrorCode.missingArg);
        }
        if (redisEnum.isPersistence()) {
            persistenceRedisTemplate.opsForList().rightPushAll(redisEnum.getRedisKey(), list);
        }
        else {
            redisTemplate.opsForList().rightPushAll(redisEnum.getRedisKey(), list);
        }
    }

    @Override
    public <T> void listRightPush(String key, boolean isPersistence, T... input) {
        if (CheckUtil.objIsEmpty(key, input)) {
            throw PangException.create(EErrorCode.missingArg);
        }

        if (isPersistence) {
            persistenceRedisTemplate.opsForList().rightPushAll(key, input);
        }
        else {
            redisTemplate.opsForList().rightPushAll(key, input);
        }
    }

    @Override
    public <T> T listLeftPop(RedisEnum redisEnum, Class<T> tClass) {
        if (CheckUtil.objIsEmpty(redisEnum, tClass)) {
            throw PangException.create(EErrorCode.missingArg);
        }

        JSONObject object = null;
        if (redisEnum.isPersistence()) {
            object = (JSONObject) persistenceRedisTemplate.opsForList().leftPop(redisEnum.getRedisKey());
        }
        else {
            object = (JSONObject) redisTemplate.opsForList().leftPop(redisEnum.getRedisKey());
        }

        if (object == null) {
            return null;
        }

        return change(object, tClass);
    }

    /**
     * vehicle 获取list数据
     *
     * @param tClass
     * @param <T>
     */
    @Override
    public <T> T listLeftPop(String key, boolean isPersistence, Class<T> tClass) {
        if (CheckUtil.objIsEmpty(key, tClass)) {
            throw PangException.create(EErrorCode.missingArg);
        }

        JSONObject object = null;
        if (isPersistence) {
            object = (JSONObject) persistenceRedisTemplate.opsForList().leftPop(key);
        }
        else {
            object = (JSONObject) redisTemplate.opsForList().leftPop(key);
        }

        if (object == null) {
            return null;
        }

        return change(object, tClass);
    }

    /**
     * 向简单类型新增或修改数据
     *
     * @param redisEnum
     * @param t
     * @param <T>
     */
    @Override
    public <T> void valAddOrUpdate(RedisEnum redisEnum, T t) {
        String key = redisEnum.getRedisKey();

        if (redisEnum.isPersistence()) {
            persistenceRedisTemplate.opsForValue().set(key, t);
        }
        else {
            redisTemplate.opsForValue().set(key, t);
        }
    }

    /**
     * 获取简单类型数据
     *
     * @param redisEnum
     * @param <T>
     * @return
     */
    @Override
    public <T> T valGet(RedisEnum redisEnum, Class<T> tClass) {
        String key = redisEnum.getRedisKey();

        if (redisEnum.isPersistence()) {
            return change((JSONObject) persistenceRedisTemplate.opsForValue().get(key), tClass);
        }
        else {
            return change((JSONObject) redisTemplate.opsForValue().get(key), tClass);
        }
    }

    /**
     * 获取简单类型数据列表
     *
     * @param redisEnum
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> valGetList(RedisEnum redisEnum, Class<T> tClass) {
        String key = redisEnum.getRedisKey();

        JSONArray jsonArray = null;

        if (redisEnum.isPersistence()) {
            jsonArray = (JSONArray) persistenceRedisTemplate.opsForValue().get(key);
        }
        else {
            jsonArray = (JSONArray) redisTemplate.opsForValue().get(key);
        }

        if (jsonArray == null) {
            return Lists.newLinkedList();
        }

        List<T> rtnList = Lists.newLinkedList();
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = (JSONObject) jsonArray.get(i);

            rtnList.add(change(jsonObject, tClass));
        }

        return rtnList;
    }


    @Override
    public void valExpire(RedisEnum redisEnum, int seconds) {
        String key = redisEnum.getRedisKey();

        if (redisEnum.isPersistence()) {
            persistenceRedisTemplate.expire(key, seconds, TimeUnit.SECONDS);
        }
        else {
            redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
        }
    }

    /**
     * 删除缓存
     *
     * @param redisEnum
     */
    @Override
    public void valDel(RedisEnum redisEnum) {
        String key = redisEnum.getRedisKey();

        if (redisEnum.isPersistence()) {
            persistenceRedisTemplate.delete(key);
        }
        else {
            redisTemplate.delete(key);
        }
    }


    private <T> T change(JSONObject jsonObject, Class<T> tClass) {
        if (jsonObject == null) {
            return null;
        }

        return jsonObject.toJavaObject(tClass);
    }

}
